///////////////////////////////////////////////////////////////////////////
//                           **** WAVPACK ****                            //
//                  Hybrid Lossless Wavefile Compressor                   //
//              Copyright (c) 1998 - 2007 Conifer Software.               //
//                          All Rights Reserved.                          //
//      Distributed under the BSD Software License (see license.txt)      //
////////////////////////////////////////////////////////////////////////////

// wputils.c

// This module provides a high-level interface to creating WavPack 4.0 streams
// or files and is optimized for a resource-limited environment. Unlike the
// standard WavPack library, this version uses no dynamic memory allocation
// and does not require the entire block's worth of samples to be visible at
// the same time. Instead, it contains buffers to build the blocks
// incrementally. Additionally, it provides logic to terminate blocks when the
// output buffers are nearly full rather than require that the output buffers
// always provide a "worst case" size. This works well because WavPack blocks
// may contain any number of samples (up to 128k) and are even reasonably
// efficient down to just a few thousand samples.

// This version of the code uses no dynamic memory allocation. Instead it
// contains two output block buffers (one for wv data and one for wvc data)
// and one static WavpackContext structure. If this code is to be used for
// multiple files at once then this must be modified.

// For writing WavPack files there are no I/O routines used; a callback for
// writing completed blocks is provided.


#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "include/wplocal.h"

///////////////////////////// local table storage ////////////////////////////

const int32_t sample_rates [] = { 6000, 8000, 9600, 11025, 12000, 16000, 22050,
    24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 };

///////////////////////////// large static storage ////////////////////////////

/*#define BIT_BUFFER_SIZE 65536   // This should be carefully chosen for the
                                // application and platform. Larger buffers are
                                // somewhat more efficient, but the code will
                                // allow smaller buffers and simply terminate
                                // blocks early. If the hybrid lossless mode
                                // (2 file) is not needed then the wvc_buffer
                                // can be made very small.

static uchar wv_buffer [BIT_BUFFER_SIZE];
// wvc_buffer [BIT_BUFFER_SIZE];
//static WavpackContext wavpack_context;
*/
///////////////////////////// executable code ////////////////////////////////

// This function returns a pointer to a string describing the last error
// generated by WavPack.

char *WavpackGetErrorMessage (WavpackContext *wpc)
{
    return wpc->error_message;
}

// Set configuration for writing WavPack files. This must be done before
// sending any actual samples. The "config" structure contains the following
// required information:

// config->bytes_per_sample     see WavpackGetBytesPerSample() for info
// config->bits_per_sample      see WavpackGetBitsPerSample() for info
// config->num_channels         self evident
// config->sample_rate          self evident

// In addition, the following fields and flags may be set: 

// config->flags:
// --------------
// o CONFIG_HYBRID_FLAG         select hybrid mode (must set bitrate)
// o CONFIG_JOINT_STEREO        select joint stereo (must set override also)
// o CONFIG_JOINT_OVERRIDE      override default joint stereo selection
// o CONFIG_HYBRID_SHAPE        select hybrid noise shaping (set override &
//                                                      shaping_weight != 0)
// o CONFIG_SHAPE_OVERRIDE      override default hybrid noise shaping
//                               (set CONFIG_HYBRID_SHAPE and shaping_weight)
// o CONFIG_FAST_FLAG           "fast" compression mode
// o CONFIG_HIGH_FLAG           "high" compression mode
// o CONFIG_VERY_HIGH_FLAG      "very high" compression mode
// o CONFIG_CREATE_WVC          create correction file
// o CONFIG_OPTIMIZE_WVC        maximize bybrid compression (-cc option)

// config->bitrate              hybrid bitrate in bits/sample (scaled up 2^8)
// config->shaping_weight       hybrid noise shaping coefficient (scaled up 2^10)
// config->block_samples        force samples per WavPack block (0 = use deflt)

// If the number of samples to be written is known then it should be passed
// here. If the duration is not known then pass -1. In the case that the size
// is not known (or the writing is terminated early) then it is suggested that
// the application retrieve the first block written and let the library update
// the total samples indication. A function is provided to do this update and
// it should be done to the "correction" file also. If this cannot be done
// (because a pipe is being used, for instance) then a valid WavPack will still
// be created, but when applications want to access that file they will have
// to seek all the way to the end to determine the actual duration. A return of
// FALSE indicates an error.

int WavpackSetConfiguration (WavpackContext *wpc, WavpackConfig *config, uint32_t total_samples)
{
    uint32_t flags = (config->bytes_per_sample - 1);
    WavpackStream *wps = &wpc->stream;
    int bps = 0, shift, i;

    wpc->total_samples = total_samples;
    wpc->config.sample_rate = config->sample_rate;
    wpc->config.num_channels = config->num_channels;
    wpc->config.bits_per_sample = config->bits_per_sample;
    wpc->config.bytes_per_sample = config->bytes_per_sample;
    wpc->config.block_samples = config->block_samples;
    wpc->config.flags = config->flags;

    if (wpc->config.flags & CONFIG_VERY_HIGH_FLAG)
        wpc->config.flags |= CONFIG_HIGH_FLAG;

    shift = (config->bytes_per_sample * 8) - config->bits_per_sample;

    for (i = 0; i < 15; ++i)
        if (wpc->config.sample_rate == sample_rates [i])
            break;

    flags |= (uint32_t) i << SRATE_LSB;
    flags |= (uint32_t) shift << SHIFT_LSB;

    flags |= HYBRID_FLAG;
    bps = config->bitrate;
    config->bitrate = 0;

    wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
    wps->wphdr.total_samples = wpc->total_samples;
    wps->wphdr.flags = flags | INITIAL_BLOCK | FINAL_BLOCK;
    wps->bits = bps;
    return TRUE;
}

// Prepare to actually pack samples by determining the size of the WavPack
// blocks and initializing the stream. Call after WavpackSetConfiguration()
// and before WavpackPackSamples(). A return of FALSE indicates an error.

int WavpackPackInit (WavpackContext *wpc)
{
    if (wpc->config.block_samples)
        wpc->block_samples = wpc->config.block_samples;
    else {
        if (wpc->config.flags & CONFIG_HIGH_FLAG)
            wpc->block_samples = wpc->config.sample_rate;
        else if (!(wpc->config.sample_rate % 2))
            wpc->block_samples = wpc->config.sample_rate / 2;
        else
            wpc->block_samples = wpc->config.sample_rate;

        while (wpc->block_samples * wpc->config.num_channels > 150000)
            wpc->block_samples /= 2;

        while (wpc->block_samples * wpc->config.num_channels < 40000)
            wpc->block_samples *= 2;
    }

    pack_init (wpc);

    return TRUE;
}

// Pack the specified samples. Samples must be stored in longs in the native
// endian format of the executing processor. The number of samples specified
// indicates composite samples (sometimes called "frames"). So, the actual
// number of data points would be this "sample_count" times the number of
// channels. Note that samples are immediately packed into the block(s)
// currently being built. If the predetermined number of sample per block
// is reached, or the block being built is approaching overflow, then the
// block will be completed and written. If an application wants to break a
// block at a specific sample, then it must simply call WavpackFlushSamples()
// to force an early termination. Completed WavPack blocks are send to the
// function provided in the initial call to WavpackOpenFileOutput(). A
// return of FALSE indicates an error.

static int finish_block (WavpackContext *wpc);

int WavpackPackSamples (WavpackContext *wpc,
                        int32_t *sample_buffer,
                        uint32_t sample_count)
{
    WavpackStream *wps = &wpc->stream;
    int nch = wpc->config.num_channels;
    uint32_t flags = wps->wphdr.flags;

    if (flags & SHIFT_MASK)
    {
        int shift = (flags & SHIFT_MASK) >> SHIFT_LSB;
        int32_t *ptr = sample_buffer;
        uint32_t cnt = sample_count;
        while (cnt--)
            *ptr++ >>= shift;
    }

    while (sample_count)
    {
        uint32_t samples_to_pack, samples_packed;

        if (!wpc->acc_samples)
        {
            flags &= ~MAG_MASK;
            flags += (1L << MAG_LSB) * ((flags & BYTES_STORED) * 8 + 7);

            wps->wphdr.block_index = wps->sample_index;
            wps->wphdr.flags = flags;
            pack_start_block (wpc);
        }

        if (wpc->acc_samples + sample_count > wpc->block_samples)
            samples_to_pack = wpc->block_samples - wpc->acc_samples;
        else
            samples_to_pack = sample_count;
   
        samples_packed = pack_samples (wpc, sample_buffer, samples_to_pack);

        sample_buffer += samples_packed * nch;
        sample_count -= samples_packed;

        if ((wpc->acc_samples += samples_packed) == wpc->block_samples ||
            samples_packed != samples_to_pack)
        {
            if (!finish_block (wpc))
            {
                return FALSE;
            }
        }
    }

    return TRUE;
}

// Flush all accumulated samples into WavPack blocks. This is normally called
// after all samples have been sent to WavpackPackSamples(), but can also be
// called to terminate a WavPack block at a specific sample (in other words it
// is possible to continue after this operation). A return of FALSE indicates
// an error.

int WavpackFlushSamples (WavpackContext *wpc)
{
    if (wpc->acc_samples && !finish_block(wpc))
    {
        return FALSE;
    }
    return TRUE;
}

static int finish_block (WavpackContext *wpc)
{
    WavpackStream *wps = &wpc->stream;
    uint32_t bcount;
    int result;

    result = pack_finish_block (wpc);
    wpc->acc_samples = 0;

    if (!result)
    {
        return result;
    }

    bcount = ((WavpackHeader *) wps->blockbuff)->ckSize + 8;
    result = wpc->blockout(wpc, wps->blockbuff, bcount);

    if (!result)
    {
        return result;
    }

    wpc->filelen += bcount;
    return result;
}

// Get total number of samples contained in the WavPack file, or -1 if unknown

uint32_t WavpackGetNumSamples (WavpackContext *wpc)
{
    return wpc ? wpc->total_samples : (uint32_t) -1;
}

// Get the current sample index position, or -1 if unknown

uint32_t WavpackGetSampleIndex (WavpackContext *wpc)
{
    if (wpc)
    {
        return wpc->stream.sample_index;
    }
    return (uint32_t) -1;
}

// Given the pointer to the first block written (to either a .wv or .wvc file),
// update the block with the actual number of samples written. This should
// be done if WavpackSetConfiguration() was called with an incorrect number
// of samples (or -1). It is the responsibility of the application to read and
// rewrite the block. An example of this can be found in the Audition filter.

void WavpackUpdateNumSamples (WavpackContext *wpc, void *first_block)
{
    ((WavpackHeader *) first_block)->total_samples = WavpackGetSampleIndex (wpc);
}

// return TRUE if any uncorrected lossy blocks were actually written or read

int WavpackLossyBlocks (WavpackContext *wpc)
{
    return wpc ? wpc->lossy_blocks : 0;
}

// Return the total size of the WavPack file(s) in bytes.

uint32_t WavpackGetFileSize (WavpackContext *wpc)
{
    return wpc ? wpc->filelen : 0;
}

// Close the specified WavPack file and release all resources used by it.
// Returns NULL.

WavpackContext *WavpackCloseFile (WavpackContext *wpc)
{
    return NULL;
}

// Returns the sample rate of the specified WavPack file

uint32_t WavpackGetSampleRate (WavpackContext *wpc)
{
    return wpc ? wpc->config.sample_rate : 44100;
}

// Returns the number of channels of the specified WavPack file.

int WavpackGetNumChannels (WavpackContext *wpc)
{
    return wpc ? wpc->config.num_channels : 2;
}

// Returns the actual number of valid bits per sample contained in the
// original file from 1 to 24, and which may or may not be a multiple
// of 8. When this value is not a multiple of 8, then the "extra" bits
// are located in the LSBs of the results. That is, values are right
// justified when unpacked into ints, but are left justified in the
// number of bytes used by the original data.

int WavpackGetBitsPerSample (WavpackContext *wpc)
{
    return wpc ? wpc->config.bits_per_sample : 16;
}

// Returns the number of bytes used for each sample (1 to 4) in the original
// file. This is required information for the user of this module because the
// audio data is returned in the LOWER bytes of the long buffer and must be
// left-shifted 8, 16, or 24 bits if normalized longs are required.

int WavpackGetBytesPerSample (WavpackContext *wpc)
{
    return wpc ? wpc->config.bytes_per_sample : 2;
}
